# -*- encoding:utf-8 -*-
# 导入views的目的原理,把每隔模块的用途解释一下
from flask import Blueprint,views,render_template,url_for,request,session,redirect,g,jsonify
# 创建表后，导入

# 创建登陆表单验证后，导入,.forms代表同级目录下的form文件
from .forms import LoginForm,ResetpwdForm,ResetMailForm
from .models import BACKUser,BACKPermission,BACKRole
from .decorators import login_required,permission_required
from flask_mail import Message

# 重置密码时优化jsonfiy数据
# 进行邮箱验证码验证时需要存储信息，使用memcache，导入lhcache
from utils import restful,lhcache

# 导入db，修改密码提交使用,导入mail
from exts import db,mail
# 登陆限制中user_id的优化，定义为变量
import config,random,string
# 创建蓝图,前缀设置为back
# 注册蓝图时back前的蓝图需要加上去，否则报错ValueError: urls must start with a leading slash
bp = Blueprint("back",__name__,url_prefix="/back")


# 路由的装饰器必须在其他装饰器的前面，位于第一位
@bp.route('/')
@login_required
def index():
    #通过 @bp.before_request钩子函数的使用，此处g.back_user已经完成，在模板中可以使用g.back_user
    return render_template('back/back_index.html')




# 注销功能的实现，注销需要登录才可以完成
@bp.route('/logout')
@login_required
def logout():
    # 清除session中的信息
    # session.clear()
    # 或者
    del session[config.BACK_USER_ID]
    # 重定向到登录页面
    # 不能写成return render_template('back/back_login.html')，否则后续无法进行登录，操作方法不允许，
    # 所以进行重定向,用url_for跳转至login视图，深度区别render_template与url_for的
    return redirect(url_for('back.login'))

# 用于邮件发送测试，编写前需要导入mail  Message
@bp.route('/email/')
def send_email():
    message=Message('神毓逍遥',recipients=['563667579@qq.com'],body='测试')
    mail.send(message)
    return 'ok'

# 邮箱验证码功能
@bp.route('/email_captcha/')
def email_captcha():
    # 与resetmail.js文件一起看，其中获取的email信息
    # 通过request请求传递
    email = request.args.get('email')
    if not email:
        # 返回json数据，使用restful，此处待定需要厘清
        return restful.param_error('请传递邮箱参数')

    # 制作图形验证码
    # 将字母大小写集合的字符串改为一个列表，其中字母单个呈现
    source = list(string.ascii_letters)
    # 将数字以字符的形式更新到列表中
    source.extend(map(lambda x:str(x) ,range(0,10)))
    # 随机选取6个字符，然后整合在一起
    captcha="".join(random.sample(source,6))

    # 给邮箱发邮件，接受email参数
    # 将验证码发送给recipient
    message=Message("python",recipients=[email],body='验证码为:%s'%captcha)
    try:
        # 发送邮件
        mail.send(message)
    except:
        # 相关信息参见restful.py
        return restful.server_error()
    # 运行至此表代表获取成功
    # 此处存储邮箱与对应的验证码，key  value
    lhcache.set(email,captcha)
    return restful.succss()


# 个人中心相关信息的视图
@bp.route('/profile')
@login_required
def profile():
    # 因为只是查看信息，并不需要更新信息，所以get方法，使用模板渲染，而不是url_for
    return render_template('back/back_profile.html')


@bp.route('/posts/')
#确认登陆后进行权限判断
@login_required
#传递参数的装饰器
@permission_required(BACKPermission.POSTER)
def posts():
    return render_template('back/back_posts.html')

@bp.route('/comments/')
@login_required
@permission_required(BACKPermission.COMMENTER)
def comments():
    return render_template('back/back_comment.html')



@bp.route('/boards/')
@login_required
@permission_required(BACKPermission.BOARDER)
def boards():
    return render_template('back/back_boarder.html')

@bp.route('/fusers/')
@login_required
@permission_required(BACKPermission.FRONTUSER)
def fusers():
    return render_template('back/back_fuser.html')


@bp.route('/busers/')
@login_required
@permission_required(BACKPermission.BACKUSER)
def busers():
    return render_template('back/back_buser.html')

@bp.route('/roles/')
@login_required
@permission_required(BACKPermission.ALL_PERMISSION)
def roles():
    return render_template('back/back_roles.html')



class LoginView(views.MethodView):
    # 当后面出现self.get(message='邮箱或密码错误')时，在定义get()函数时。里面需要设置massage参数，
    # 默认设置为None，不传massage参数也是可以的
    def get(self,message=None):
        # 当get函数中定义message时，模板中也需要传入message，
        # 否则前端渲染时无法将massage出入，然后再back_login.html中进行渲染
        return render_template('back/back_login.html',message=message)

    def post(self):
        # 将相关form相关信息导入
        form =LoginForm(request.form)
        # 若验证成功，则获取相关信息
        if form.validate():
            email = form.email.data
            password = form.password.data
            remember =form.remember.data
            # 判断用户在模型中是否存在,按照邮件相同查找，取出第一个
            user = BACKUser.query.filter_by(email=email).first()
            # 如果用户存在且验证码验证成功则
            if user and user.check_password(password):
                # 如果成功则用session保存用户信息
                session[config.BACK_USER_ID]=user.id
                # 若session.permanent = True，过期时间31天
                if remember:
                    session.permanent = True
                # 若写成index则无法跳转，需要[蓝图].[视图函数]，如下back.index
                # k可以与重置密码对比，次数没有数据库信息需要更改，则没有用到ajax
                # 此处采用重定向的方式
                return redirect(url_for('back.index'))
            else:
                # 邮箱密码格式正确，但是输入错误，则返回当前页面，通过self.get()函数优
                # 化，不用return到其他页面，提高用户感受，另外不用写成 return render_template
                # ('back/back_login.html')，进行了优化,通过self.get()渲染登陆页面
                return self.get(message='邮箱或密码错误')
        else:
            # print(form.errors)
            # form.errors为{'password': ['请输入正确的格式密码']}，字典中，前面的是元组，后面是列表

            # popitem随机取值,[1][0]进行索引，取值'请输入正确的格式密码'
            # message = form.errors.popitem()[1][0]
            # 继承baseform父类后进行改写
            message = form.get_error()
            # 若邮箱或密码格式错误，没有
            # 通过验证则，登陆失败，则重新渲染get函数下的模板
            return self.get(message=message)


class ResetPwdView(views.MethodView):
    #这句话作用
    decorators = [login_required]
    def get(self):
        return render_template('back/back_resetpwd.html')

    def post(self):
        # 请求获得表达信息
        form = ResetpwdForm(request.form)
        # 对获取的表单信息进行验证，若旧密码输入正确，新密码输入正确，则
        # 验证表单格式是否正确，按照forms中定义的进行验证
        if form.validate():
            # 若验证格式正确，则获取用户输入的旧密码
            oldpwd = form.oldpwd.data
            # 则获取用户输入的新密码的信息
            newpwd = form.newpwd1.data
            # newpwd2 = form.newpwd2.data   此处不需要，之前已经进行了两者的验证
            # 获取登陆用户信息
            user = g.back_user
            # 进行旧密码与登陆用户密码进行验证
            if user.check_password(oldpwd):
                # 验证相同，则进行更改
                user.password = newpwd
                # 提交新密码
                db.session.commit()
                # 提交数据库成功，则返回jsonify数据
                # return jsonify({"code":200, "message":""})

                # 优化之一：
                # return restful.restful_result(200,message="",data=None)

                # 进一步优化，定义具体的状态码返回值后：
                return restful.succss()

            # 密码错误，则返回参数错误状态码，输出旧密码错误信息
            else:
                # return jsonify({"code":400,"message":"旧密码输入错误"})

                # 进一步优化，定义具体的状态码返回值后：
                return restful.param_error("旧密码输入错误")

        # 表单验证失败
        else:
            # message = form.get_error()
            # 为什么login是return get等 ，而此处需要用jsonfiy
            # return jsonify({"code":400,"message":message})

            # 进一步优化，定义具体的状态码返回值后：
            return restful.param_error(form.get_error())


# 定义修改邮箱的视图
class ResetMailView(views.MethodView):
    decorators = [login_required]
    def get(self):
        return render_template('back/back_resetmail.html')

    def post(self):
        form = ResetMailForm(request.form)
        # 若表单验证成功,将验证成功的信息提交给数据库
        if form.validate():
            email=form.email.data
            g.back_user.email=email
            db.session.commit()
            return restful.succss()
        else:
            return restful.param_error(form.get_error())

# 若果纯app（IOS andorid）平台，可以使用flask-restful，数据交换都是用的json方法，
# 此处又有模板等  所以要使这种方法

# 把视图添加到映射中
# 不可写成‘login’，里面的//不可忘记，否则返回404
# 此信息代表客户端在浏览网页时，服务器无法正常提供信息，或是服务器无法回应且不知原因
bp.add_url_rule('/login/',view_func=LoginView.as_view('login'))
bp.add_url_rule('/resetpwd/',view_func=ResetPwdView.as_view('resetpwd'))
bp.add_url_rule('/resetemail/',view_func=ResetMailView.as_view('resetmail'))
